using System;
using System.Collections.Generic;
using System.Collections;
using System.Collections.ObjectModel;
using System.Text;

using System.Drawing;

using Athena.SBW;
using Athena.Core;



namespace Athena.IO
{
    class SBMLReader : Athena.IO.IModelReader
    {
        private static SBMLReader _oInstance = null;
        public static SBMLReader Instance
        {
            get
            {
                if (_oInstance == null) _oInstance = new SBMLReader();
                return _oInstance;
            }
        }
        private SBMLReader()
        {
        }

        string _sCurrentSBML;

        private static SBMLExtension.SBMLLayout SBMLLayout
        {
            get
            {
                return SBMLExtension.SBMLLayout.Instance;
            }
        }
        public Model Read(string sSBML)
        {
            long nTick = DateTime.Now.Ticks;

            _sCurrentSBML = sSBML;
            SBMLSupport.loadSBML(_sCurrentSBML);
            SBMLLayout.loadSBML(_sCurrentSBML);
            if (!SBMLLayout.hasLayout())
            {
                AutoLayout.loadSBML(_sCurrentSBML);
                _sCurrentSBML = AutoLayout.generateLayout(15F, 50F, false);
                SBMLLayout.loadSBML(_sCurrentSBML);
            }

            List<Species> oSpeciesList = PopulateSpeciesList();
            List<Reaction> oReactionList = PopulateReactionList();
            List<Parameter> oParameterList = PopulateParameterList();

            Model model = new Model();

            AddAllSpeciesToModel(model, oSpeciesList);

            AddReactionsToModel(model, oReactionList);

            AddParametersToModel(model, oParameterList);

            System.Diagnostics.Debug.WriteLine(String.Format("Loading model took: {0} ms.", new TimeSpan(DateTime.Now.Ticks - nTick).TotalMilliseconds));

            return model;
        }

        private static void AddAllSpeciesToModel(Model model, List<Species> oSpeciesList)
        {
            foreach (Species s in oSpeciesList)
            {
                AddSpeciesToModel(model, s);
            }
        }

        private static void AddSpeciesToModel(Model model, Species species)
        {
            Objects.SpeciesGlyph speciesGlyph = new Athena.Objects.SpeciesGlyph(species.Location);
            speciesGlyph.Name = species.Name;
            speciesGlyph.Text = species.Name;
            speciesGlyph.Boundary = species.Boundary;
            speciesGlyph.Dimensions = species.Dimensions;
            speciesGlyph.Concentration = species.Concentration;
            model.AddToModel(speciesGlyph, false);
        }

        private static void AddReactionsToModel(Model model, List<Reaction> oReactionList)
        {
            foreach (Reaction reaction in oReactionList)
            {
                List<Objects.IDrawable> inputs = model.FindItemsWithIDs(reaction.Inputs),
                                        outputs = model.FindItemsWithIDs(reaction.Outputs);

                Objects.BezierConnector connector = new Athena.Objects.BezierConnector(inputs, outputs);
                connector.Rate = reaction.Rate;
                if (connector.Item is Edge)
                {
                    //((Edge)connector.Item).ClearParameters();

                    for (int i = 0; i < reaction.ParameterIds.Count; ++i)
                    {
                        ((Edge)connector.Item).SetParameter(reaction.ParameterIds[i], reaction.ParameterValues[i]);
                    }
                    for (int i = 0; i < reaction.Inputs.Count; ++i)
                    {
                        connector.SetReactantStoichiometry(reaction.Inputs[i], 0, false);
                    }
                    for (int i = 0; i < reaction.Outputs.Count; ++i)
                    {
                        connector.SetProductStoichiometry(reaction.Outputs[i], 0, false);
                    }
                    for (int i = 0; i < reaction.Inputs.Count; ++i)
                    {
                        connector.SetReactantStoichiometry(reaction.Inputs[i], reaction.ReactantStoichiometry[i], true);
                    }
                    for (int i = 0; i < reaction.Outputs.Count; ++i)
                    {
                        connector.SetProductStoichiometry(reaction.Outputs[i], reaction.ProductStoichiometry[i], true);
                    }
                }
                connector.Name = reaction.Name;
                connector.Text = reaction.Name;
                int numReactants = 0, numProducts = 0, numModifiers = 0;

                //Read curve information
                try
                {
                    List<Objects.BezierConnector.CurveSegment> visited = new List<Athena.Objects.BezierConnector.CurveSegment>();
                    //count the number of reactant,product and modifier curves                    
                    foreach (NamedPair np in reaction.CurveSegments)
                    {
                        if (np.LineSegments.Count > 0)
                        {
                            switch (np.Role)
                            {
                                case "substrate":
                                    ++numReactants;
                                    break;
                                case "product":
                                    ++numProducts;
                                    break;
                                default:
                                    ++numModifiers;
                                    break;
                            }
                        }
                        else
                        {
                        }
                    }
                    if (numReactants == 0 && numProducts > 0)
                    {
                        List<Objects.BezierConnector.CurveSegment> inputCurves = new List<Athena.Objects.BezierConnector.CurveSegment>();
                        foreach (NamedPair np in reaction.CurveSegments)
                        {
                            #region replace product curves
                            if (np.Role == "product")
                            {
                                //get all the BezierConnector's curves that are connected to np.Name
                                List<Objects.BezierConnector.CurveSegment> curves = connector.FindOutputCurveSegments(np.Name);

                                //find the BezierConnector's curve that is connected to np.Name but has not been visited yet
                                foreach (Objects.BezierConnector.CurveSegment curve in curves)
                                {
                                    if (!visited.Contains(curve))
                                    {
                                        visited.Add(curve);      //record that this curve has been visited

                                        //erase the existing curve
                                        curve.Segments.Clear();

                                        //lets redefine the curve points
                                        bool first = true;
                                        foreach (LineSegment ls in np.LineSegments)
                                        {
                                            Objects.BezierConnector.CurveSegment halfcurve = new Athena.Objects.BezierConnector.CurveSegment(
                                                                                                    curve.StartNode,
                                                                                                    new Athena.Objects.Connector.ControlPoint(ls.End),
                                                                                                    new Athena.Objects.Connector.ControlPoint(ls.Start),
                                                                                                    new Athena.Objects.Connector.ControlPoint(ls.Start));
                                            if (ls is BezierSegment)
                                            {
                                                if (first)
                                                {
                                                    Objects.BezierConnector.CurveSegment[] twohalves = Objects.BezierConnector.GetHalfCurve(ls.Start, ls.End, ((BezierSegment)ls).ControlPoint1, ((BezierSegment)ls).ControlPoint2, curve.StartNode);
                                                    halfcurve = twohalves[0];
                                                    inputCurves.Add(twohalves[1]);
                                                    first = false;
                                                }
                                                else
                                                {
                                                    halfcurve = new Objects.BezierConnector.CurveSegment(
                                                                    curve.StartNode,
                                                                    new Objects.Connector.ControlPoint(((BezierSegment)ls).ControlPoint2),
                                                                    new Objects.Connector.ControlPoint(((BezierSegment)ls).ControlPoint1),
                                                                    new Objects.Connector.ControlPoint(ls.Start));
                                                }
                                            }
                                            else
                                            {
                                                curve.IsStraight = true;
                                            }
                                            curve.Segments.Add(new Objects.BezierConnector.CurveSegment.Segment(
                                                    halfcurve.Segments[0]));
                                        }
                                        break;
                                    }
                                }
                            }
                            #endregion
                            #region replace reactant curves
                            bool allstraight = true;
                            foreach (Objects.BezierConnector.CurveSegment segment in connector.ProductCurveSegments)
                            {
                                if (!segment.IsStraight) allstraight = false;
                            }
                            if (allstraight)
                                foreach (Objects.BezierConnector.CurveSegment segment in connector.ReactantCurveSegments)
                                    segment.IsStraight = true;

                            foreach (Objects.BezierConnector.CurveSegment segment in inputCurves)
                            {
                                foreach (Objects.BezierConnector.CurveSegment curve in connector.ReactantCurveSegments)
                                {
                                    if (!visited.Contains(curve))
                                    {
                                        curve.Segments.Clear();
                                        curve.Segments.AddRange(segment.Segments);
                                    }
                                }
                            }
                            #endregion
                        }
                    }
                    else
                        if (numReactants > 0 && numProducts == 0)
                        {
                            List<Objects.BezierConnector.CurveSegment> outputCurves = new List<Athena.Objects.BezierConnector.CurveSegment>();
                            foreach (NamedPair np in reaction.CurveSegments)
                            {
                                #region replace substrate curves
                                if (np.Role == "substrate")
                                {
                                    //get all the BezierConnector's curves that are connected to np.Name
                                    List<Objects.BezierConnector.CurveSegment> curves = connector.FindInputCurveSegments(np.Name);

                                    //find the BezierConnector's curve that is connected to np.Name but has not been visited yet
                                    foreach (Objects.BezierConnector.CurveSegment curve in curves)
                                    {
                                        if (!visited.Contains(curve))
                                        {
                                            visited.Add(curve);      //record that this curve has been visited

                                            //erase the existing curve
                                            curve.Segments.Clear();

                                            //lets redefine the curve points
                                            bool first = true;
                                            foreach (LineSegment ls in np.LineSegments)
                                            {
                                                Objects.BezierConnector.CurveSegment halfcurve = new Athena.Objects.BezierConnector.CurveSegment(
                                                                                                        curve.StartNode,
                                                                                                        new Athena.Objects.Connector.ControlPoint(ls.End),
                                                                                                        new Athena.Objects.Connector.ControlPoint(ls.Start),
                                                                                                        new Athena.Objects.Connector.ControlPoint(ls.Start));
                                                if (ls is BezierSegment)
                                                {
                                                    if (first)
                                                    {
                                                        Objects.BezierConnector.CurveSegment[] twohalves = Objects.BezierConnector.GetHalfCurve(ls.Start, ls.End, ((BezierSegment)ls).ControlPoint1, ((BezierSegment)ls).ControlPoint2, curve.StartNode);
                                                        halfcurve = twohalves[1];
                                                        outputCurves.Add(twohalves[0]);
                                                        first = false;
                                                    }
                                                    else
                                                    {
                                                        halfcurve = new Objects.BezierConnector.CurveSegment(
                                                                        curve.StartNode,
                                                                        new Objects.Connector.ControlPoint(((BezierSegment)ls).ControlPoint2),
                                                                        new Objects.Connector.ControlPoint(((BezierSegment)ls).ControlPoint1),
                                                                        new Objects.Connector.ControlPoint(ls.End));
                                                    }
                                                }
                                                else
                                                {
                                                    curve.IsStraight = true;
                                                }
                                                curve.Segments.Add(new Objects.BezierConnector.CurveSegment.Segment(
                                                        halfcurve.Segments[0]));
                                            }
                                            break;
                                        }
                                    }
                                }
                                #endregion
                                #region replace product curves
                                bool allstraight = true;
                                foreach (Objects.BezierConnector.CurveSegment segment in connector.ReactantCurveSegments)
                                {
                                    if (!segment.IsStraight) allstraight = false;
                                }
                                if (allstraight)
                                    foreach (Objects.BezierConnector.CurveSegment segment in connector.ProductCurveSegments)
                                        segment.IsStraight = true;

                                foreach (Objects.BezierConnector.CurveSegment segment in outputCurves)
                                {
                                    foreach (Objects.BezierConnector.CurveSegment curve in connector.ProductCurveSegments)
                                    {
                                        if (!visited.Contains(curve))
                                        {
                                            curve.Segments.Clear();
                                            curve.Segments.AddRange(segment.Segments);
                                        }
                                    }
                                }
                                #endregion
                            }
                        }
                        else
                            if (numReactants > 0 && numProducts > 0)
                            {
                                #region assign NamedPair points to the BezierCurve curves
                                foreach (NamedPair np in reaction.CurveSegments)
                                {
                                    if (np.Role == "substrate" || np.Role == "product")
                                    {
                                        //get all the BezierConnector's curves that are connected to np.Name
                                        List<Objects.BezierConnector.CurveSegment> curves;
                                        if (np.Role == "substrate")
                                            curves = connector.FindInputCurveSegments(np.Name);
                                        else
                                            curves = connector.FindOutputCurveSegments(np.Name);

                                        //find the BezierConnector's curve that is connected to np.Name but has not been visited yet
                                        foreach (Objects.BezierConnector.CurveSegment curve in curves)
                                        {
                                            if (!visited.Contains(curve))
                                            {
                                                visited.Add(curve);      //record that this curve has been visited

                                                //erase the existing curve
                                                curve.Segments.Clear();

                                                //lets redefine the curve points
                                                foreach (LineSegment ls in np.LineSegments)
                                                {
                                                    if (np.Role == "substrate")
                                                    {
                                                        if (ls is BezierSegment)
                                                        {
                                                            curve.Segments.Add(
                                                                new Athena.Objects.BezierConnector.CurveSegment.Segment(
                                                                    ((BezierSegment)ls).Start,
                                                                    ((BezierSegment)ls).ControlPoint1,
                                                                    ((BezierSegment)ls).ControlPoint2,
                                                                    ((BezierSegment)ls).End));

                                                        }
                                                        else
                                                        {
                                                            curve.IsStraight = true;
                                                            curve.Segments.Add(
                                                                new Athena.Objects.BezierConnector.CurveSegment.Segment(
                                                                    ((LineSegment)ls).Start,
                                                                    ((LineSegment)ls).Start,
                                                                    ((LineSegment)ls).End,
                                                                    ((LineSegment)ls).End));
                                                        }
                                                    }
                                                    else
                                                    {
                                                        if (ls is BezierSegment)
                                                        {
                                                            curve.Segments.Add(
                                                                new Athena.Objects.BezierConnector.CurveSegment.Segment(
                                                                    ((BezierSegment)ls).End,
                                                                    ((BezierSegment)ls).ControlPoint2,
                                                                    ((BezierSegment)ls).ControlPoint1,
                                                                    ((BezierSegment)ls).Start));
                                                        }
                                                        else
                                                        {
                                                            curve.IsStraight = true;
                                                            curve.Segments.Add(
                                                                new Athena.Objects.BezierConnector.CurveSegment.Segment(
                                                                    ((LineSegment)ls).End,
                                                                    ((LineSegment)ls).End,
                                                                    ((LineSegment)ls).Start,
                                                                    ((LineSegment)ls).Start));
                                                        }
                                                    }
                                                }
                                                break;
                                            }
                                        }
                                    }
                                }
                                #endregion
                            }
                }
                catch (Exception)
                {
                }
                connector.FixMiddleSegment();

                model.AddToModel(connector, false);

                foreach (Objects.IDrawable id in connector.Reactants)
                {
                    if (!model.Contains(id)) model.AddToModel(id);
                }
                foreach (Objects.IDrawable id in connector.Products)
                {
                    if (!model.Contains(id)) model.AddToModel(id);
                }

                if (numModifiers > 0)
                {
                    foreach (NamedPair np in reaction.CurveSegments)
                    {
                        if (np.Role == "modifier" || np.Role == "activator" || np.Role == "inhibitor")
                        {
                            #region assign np points to curves
                            //Objects.BezierConnector modifier = new Athena.Objects.BezierConnector(model.FindItemWithID(np.Name), connector);
                            Objects.IDrawable id = model.FindItemWithID(np.Name);
                            if (id != null)
                                connector.AddModifier(id);

                            if (!model.Contains(id)) model.AddToModel(id);

                            if (np.Role == "inhibitor")
                            {
                                List<Objects.BezierConnector.CurveSegment> cslist = connector.FindCurveSegments(id);
                                foreach (Objects.BezierConnector.CurveSegment cs in cslist)
                                    cs.ArrowHead = Athena.Objects.Connector.ArrowHeadType.Bar;
                            }
                            else
                            {
                                List<Objects.BezierConnector.CurveSegment> cslist = connector.FindCurveSegments(id);
                                foreach (Objects.BezierConnector.CurveSegment cs in cslist)
                                    cs.ArrowHead = Athena.Objects.Connector.ArrowHeadType.Circle;
                            }

                            try
                            {
                                List<Objects.BezierConnector.CurveSegment> visited2 = new List<Athena.Objects.BezierConnector.CurveSegment>();
                                List<Objects.BezierConnector.CurveSegment> inputCurves = new List<Athena.Objects.BezierConnector.CurveSegment>();
                                //get all the BezierConnector's curves that are connected to np.Name
                                List<Objects.BezierConnector.CurveSegment> curves = connector.ModifierCurveSegments;

                                //find the BezierConnector's curve that is connected to np.Name but has not been visited yet
                                foreach (Objects.BezierConnector.CurveSegment curve in curves)
                                {
                                    if (!visited2.Contains(curve))
                                    {
                                        visited2.Add(curve);      //record that this curve has been visited

                                        //erase the existing curve
                                        curve.Segments.Clear();

                                        //lets redefine the curve points
                                        //bool first = true;

                                        //np.LineSegments.Reverse();

                                        foreach (LineSegment ls in np.LineSegments)
                                        {
                                            if (ls is BezierSegment)
                                            {
                                                /*if (first)
                                                {
                                                    Objects.BezierConnector.CurveSegment[] twohalves = Objects.BezierConnector.GetHalfCurve(ls.Start, ls.End, ((BezierSegment)ls).ControlPoint1, ((BezierSegment)ls).ControlPoint2, curve.StartNode);
                                                    curve.Segments.AddRange(twohalves[1].Segments);
                                                    inputCurves.Add(twohalves[0]);
                                                    first = false;
                                                }
                                                else
                                                {*/
                                                curve.Segments.Add(
                                                    new Athena.Objects.BezierConnector.CurveSegment.Segment(
                                                                ls.Start,
                                                                ((BezierSegment)ls).ControlPoint1,
                                                                ((BezierSegment)ls).ControlPoint2,
                                                                ls.End));
                                                //}
                                            }
                                            else
                                            {
                                                //bool skip = false;
                                                /*if (first)
                                                {
                                                    if (np.LineSegments.Count > 1)
                                                    {
                                                        inputCurves.Add(new Athena.Objects.BezierConnector.CurveSegment(
                                                                                                        curve.StartNode,
                                                                                                        new Athena.Objects.Connector.ControlPoint(ls.Start),
                                                                                                        new Athena.Objects.Connector.ControlPoint(ls.Start),
                                                                                                        new Athena.Objects.Connector.ControlPoint(ls.End)));
                                                        skip = true;
                                                    }
                                                    else
                                                    {
                                                        PointF midpt = new PointF((ls.Start.X + ls.End.X) / 2, (ls.Start.Y + ls.End.Y) / 2);
                                                        inputCurves.Add(new Athena.Objects.BezierConnector.CurveSegment(
                                                                                                        curve.StartNode,
                                                                                                        new Athena.Objects.Connector.ControlPoint(ls.Start),
                                                                                                        new Athena.Objects.Connector.ControlPoint(ls.Start),
                                                                                                        new Athena.Objects.Connector.ControlPoint(midpt)));
                                                        curve.Segments.Add(new Athena.Objects.BezierConnector.CurveSegment.Segment(
                                                                                                        ls.End, ls.End, ls.End, midpt));
                                                    }
                                                    first = false;
                                                    skip = true;
                                                }
                                                */
                                                curve.IsStraight = true;

                                                //if (!skip)
                                                //{
                                                curve.Segments.Add(
                                                    new Athena.Objects.BezierConnector.CurveSegment.Segment(
                                                    ls.Start, ls.Start, ls.End, ls.End));
                                                //}
                                            }

                                        }
                                        //curve.Segments.Reverse();
                                        break;
                                    }
                                }
                            #endregion
                                #region replace reactant curves
                                /*bool allstraight = true;
                                
                                foreach (Objects.BezierConnector.CurveSegment segment in curves)
                                {
                                    if (!segment.IsStraight) allstraight = false;
                                }
                                if (allstraight)
                                    foreach (Objects.BezierConnector.CurveSegment segment in curves)
                                        segment.IsStraight = true;
                                

                                foreach (Objects.BezierConnector.CurveSegment segment in inputCurves)
                                {
                                    foreach (Objects.BezierConnector.CurveSegment curve in curves)
                                    {
                                        curve.Segments.Clear();
                                        curve.Segments.AddRange(segment.Segments);
                                        break;
                                    }
                                }*/
                                #endregion
                            }
                            catch (Exception)
                            {
                            }
                        }
                    }

                }

                connector.DoneModifying(Objects.Corner.None, false);
            }
        }



        private static void AddParametersToModel(Model model, List<Parameter> oParameterList)
        {
            foreach (Parameter p in oParameterList)
            {
                model.SetParameter(p.Id, p.Value);
            }
        }

        private List<Reaction> PopulateReactionList()
        {
            List<Reaction> oResult = new List<Reaction>();

            ArrayList oReactionInfo = SBMLSupport.getListOfReactionInfo();

            for (int i = 0; i < oReactionInfo.Count; i++)
            {
                try
                {
                    oResult.Add(new Reaction((ArrayList)oReactionInfo[i]));
                }
                catch (Exception)
                {
                    // ignoring unnamed reactions
                }
            }

            return oResult;
        }

        private List<Species> PopulateSpeciesList()
        {
            List<Species> oResult = new List<Species>();

            ArrayList oBoundary = SBMLSupport.getListOfBoundarySpecies();
            ArrayList oFloating = SBMLSupport.getListOfFloatingSpecies();

            for (int i = 0; i < SBMLSupport.getNumBoundarySpecies(); i++)
            {
                ReadSpecies(oResult, i, true, oBoundary);
            }
            for (int i = 0; i < SBMLSupport.getNumFloatingSpecies(); i++)
            {
                ReadSpecies(oResult, i, false, oFloating);
            }

            return oResult;
        }


        private List<Parameter> PopulateParameterList()
        {
            List<Parameter> oResult = new List<Parameter>();

            ArrayList oParameterList = SBMLSupport.getListOfParameters();

            for (int i = 0; i < oParameterList.Count; i++)
            {
                oResult.Add(new Parameter(
                    (string)((ArrayList)oParameterList[i])[0],
                    (double)((ArrayList)oParameterList[i])[1]
                    ));
            }

            return oResult;
        }

        private void ReadSpecies(List<Species> oResult, int i, bool bIsBoundary, ArrayList oSpeciesInformation)
        {
            try
            {
                string sId = (string)((ArrayList)oSpeciesInformation[i])[0];
                ArrayList oList = SBMLLayout.getSpeciesGlyphById(sId);

                Species oSpecies = new Species(sId, bIsBoundary,
                    (double)((ArrayList)oSpeciesInformation[i])[1],
                    ConvertToPoint((double[])oList[2]),
                    ConvertToDimension((double[])oList[3])
                );
                oResult.Add(oSpecies);
            }
            catch (Exception)
            {
                // NEVERMIND
            }
        }

        private static void ReadSpecies(List<Species> oResult, int i, bool bIsBoundary)
        {

            try
            {
                string sId = (bIsBoundary ? SBMLSupport.getNthBoundarySpeciesId(i) : SBMLSupport.getNthFloatingSpeciesId(i));
                ArrayList oList = SBMLLayout.getSpeciesGlyphById(sId);

                Species oSpecies = new Species(sId, bIsBoundary,
                    SBMLSupport.getValue(sId),
                    ConvertToPoint((double[])oList[2]),
                    ConvertToDimension((double[])oList[3])
                );
                oResult.Add(oSpecies);
            }
            catch (Exception )
            {
                // NEVERMIND
            }
        }

        static SizeF ConvertToDimension(double[] array)
        {
            return new SizeF((float)array[0], (float)array[1]);
        }
        static PointF ConvertToPoint(double[] array)
        {
            return new PointF((float)array[0], (float)array[1]);
        }

        internal class Parameter
        {
            private string _Id;
            public string Id
            {
                get { return _Id; }
                set { _Id = value; }
            }
            private double _Value;
            public double Value
            {
                get { return _Value; }
                set { _Value = value; }
            }

            public override string ToString()
            {
                return _Id;
            }

            /// <summary>
            /// Initializes a new instance of the Parameter class.
            /// </summary>
            /// <param name="id"></param>
            /// <param name="value"></param>
            public Parameter(string id, double value)
            {
                _Id = id;
                _Value = value;
            }
        }

        internal class Species
        {
            /// <summary>
            /// Initializes a new instance of the Species class.
            /// </summary>
            /// <param name="name"></param>
            /// <param name="boundary"></param>
            /// <param name="concentration"></param>
            /// <param name="location"></param>
            /// <param name="dimensions"></param>
            public Species(string name, bool boundary, double concentration, System.Drawing.PointF location, System.Drawing.SizeF dimensions)
            {
                _Name = name;
                _Boundary = boundary;
                _Concentration = concentration;
                _Dimensions = dimensions;
                _Location = location;
            }
            private string _Name;
            public string Name
            {
                get { return _Name; }
                set { _Name = value; }
            }
            private bool _Boundary;
            public bool Boundary
            {
                get { return _Boundary; }
                set { _Boundary = value; }
            }
            private double _Concentration;
            public double Concentration
            {
                get { return _Concentration; }
                set { _Concentration = value; }
            }

            private System.Drawing.SizeF _Dimensions;
            public System.Drawing.SizeF Dimensions
            {
                get { return _Dimensions; }
                set { _Dimensions = value; }
            }

            private System.Drawing.PointF _Location;
            public System.Drawing.PointF Location
            {
                get { return _Location; }
                set { _Location = value; }
            }
        }

        internal class LineSegment
        {
            private PointF _Start;
            public PointF Start
            {
                get { return _Start; }
                set { _Start = value; }
            }

            private PointF _EndPoint;
            public PointF End
            {
                get { return _EndPoint; }
                set { _EndPoint = value; }
            }
            public LineSegment(PointF oStart, PointF oEnd)
            {
                _Start = oStart;
                _EndPoint = oEnd;
            }
        }

        internal class BezierSegment : LineSegment  
        {
            private PointF _ControlPoint1;
            public PointF ControlPoint1
            {
                get { return _ControlPoint1; }
                set { _ControlPoint1 = value; }
            }
            private PointF _ControlPoint2;
            public PointF ControlPoint2
            {
                get { return _ControlPoint2; }
                set { _ControlPoint2 = value; }
            }

            public BezierSegment(PointF oStart, PointF oEnd, PointF oControlPoint1, PointF oControlPoint2) : base(oStart, oEnd)
            {
                _ControlPoint1 = oControlPoint1;
                _ControlPoint2 = oControlPoint2;
            }
        }

        internal class NamedPair
        {
        	private string _Name;
            public string Name
            {
            	get {	return _Name; }
            	set { _Name = value;	}
            }

            private string _Role;
            public string Role
            {
                get { return _Role; }
                set { _Role = value; }
            }

            private List<LineSegment> _LineSegments;
            public List<LineSegment> LineSegments
            {
            	get {	return _LineSegments; }
            	set { _LineSegments = value;	}
            }

            public NamedPair(string sName, List<LineSegment> oList, string sRole)
            {
                _Name = sName;
                _LineSegments = oList;
                _Role = sRole;
            }

        	public  NamedPair(string sName, List<LineSegment> oList)
        	{
                _Name = sName;
                _LineSegments = oList;
        	}
        }

        internal class Reaction
        {
            private string _Name;
            public string Name
            {
                get { return _Name; }
                set { _Name = value; }
            }

            private List<string> _Inputs;
            public List<string> Inputs
            {
                get { return _Inputs; }
                set { _Inputs = value; }
            }
            private List<string> _Outputs;
            public List<string> Outputs
            {
                get { return _Outputs; }
                set { _Outputs = value; }
            }

            private List<string> _Modifiers;
            public List<string> Modifiers
            {
                get { return _Modifiers; }
                set { _Modifiers = value; }
            }

            private List<string> _ParameterIds;
            public List<string> ParameterIds
            {
                get { return _ParameterIds; }
                set { _ParameterIds = value; }
            }

            private List<double> _ParameterValues;
            public List<double> ParameterValues
            {
                get { return _ParameterValues; }
                set { _ParameterValues = value; }
            }

            private List<double> _ReactantStoichiometry;
            public List<double> ReactantStoichiometry
            {
                get { return _ReactantStoichiometry; }
                set { _ReactantStoichiometry = value; }
            }

            private List<double> _ProductStoichiometry;
            public List<double> ProductStoichiometry
            {
                get { return _ProductStoichiometry; }
                set { _ProductStoichiometry = value; }
            }

            private List<NamedPair> _CurveSegments;
            public List<NamedPair> CurveSegments
            {
                get { return _CurveSegments; }
                set { _CurveSegments = value; }
            }

            private string _Rate;
            public string Rate
            {
                get { return _Rate; }
                set { _Rate = value; }
            }

            static Utility.ListConverter<String> _oConvert = new Utility.ListConverter<string>();

            public Reaction(ArrayList oReactionInfo)
            {
                _Name = (string)oReactionInfo[0];
                _Rate = (string)oReactionInfo[3];

                if (string.IsNullOrEmpty(_Name))
                    throw new ArgumentException("Currently only named reactions are supported");

                _Modifiers = new List<string>();
                _Inputs = new List<string>();
                _Outputs = new List<string>();
                _ParameterIds = new List<string>();
                _ParameterValues = new List<double>();
                _ReactantStoichiometry = new List<double>();
                _ProductStoichiometry = new List<double>();

                ArrayList oReactantList = (ArrayList)oReactionInfo[4];
                ArrayList oProductList = (ArrayList)oReactionInfo[5];
                ArrayList oModifierList = (ArrayList)oReactionInfo[6]; 

                FillIdListFromArrayLists(_Inputs, oReactantList);                
                FillIdListFromArrayLists(_Outputs, oProductList);                
                FillIdListFromStringLists(_Modifiers, oModifierList);

                ArrayList oLocalParameters = (ArrayList)oReactionInfo[7];

                int numParameters = oLocalParameters.Count;

                for (int j = 0; j < numParameters; ++j)
                {
                    _ParameterIds.Add((string)((ArrayList)oLocalParameters[j])[0]);
                    _ParameterValues.Add((double)((ArrayList)oLocalParameters[j])[1]);
                }


                // TODO: Check this ... should this be done twice???
                //for (int j = 0; j < SBMLSupport.getNumParameters(i); ++j)
                //{
                //    _ParameterIds.Add(SBMLSupport.getNthParameterId(i, j));
                //    _ParameterValues.Add(SBMLSupport.getNthParameterValue(i, j));
                //}
                for (int j = 0; j < oReactantList.Count; ++j)
                {
                    _ReactantStoichiometry.Add((double)((ArrayList)oReactantList[j])[1]);
                }
                for (int j = 0; j < oProductList.Count; ++j)
                {
                    _ProductStoichiometry.Add((double)((ArrayList)oProductList[j])[1]);
                }

                _CurveSegments = new List<NamedPair>();

                try
                {
                    int nGlyphIndex = GetGlyphIndexForId(_Name);
                    int nSpeciesReferences = (int)SBMLLayout.getReactionGlyph(nGlyphIndex)[2];

                    List<LineSegment> oSegments = ConvertLineSegments(SBMLLayout.getReactionCurve(nGlyphIndex));
                    _CurveSegments.Add(new NamedPair("middleSegment", oSegments));

                    for (int j = 0; j < nSpeciesReferences; j++)
                    {
                        AddSpeciesReferences(_CurveSegments, nGlyphIndex, j);
                    }
                }
                catch (Exception)
                {
                    // DUNNO
                }

                // add modifiers
                foreach (NamedPair pair in _CurveSegments)
                {
                    if (pair.Role == null) continue;
                    string pairRoleToLower = pair.Role.ToLower();
                    if (pairRoleToLower.Contains("activator") || pairRoleToLower.Contains("inhibitor") || pairRoleToLower.Contains("modifier"))
                    {
                        if (!Modifiers.Contains(pair.Name))
                        {
                            Modifiers.Add(pair.Name);
                        }
                    }
                }


            }

            public Reaction(int i)
            {
                _Name = SBMLSupport.getNthReactionId(i);
                _Rate = SBMLSupport.getKineticLaw(i);
                
                if (string.IsNullOrEmpty(_Name))
                    throw new ArgumentException("Currently only named reactions are supported");

                _Modifiers = new List<string>();
                _Inputs = new List<string>();
                _Outputs = new List<string>();
                _ParameterIds = new List<string>();
                _ParameterValues = new List<double>();
                _ReactantStoichiometry = new List<double>();
                _ProductStoichiometry = new List<double>();
                                                
                FillIdListFromArrayLists(_Inputs, SBMLSupport.getNthListOfReactants(i));
                FillIdListFromArrayLists(_Outputs,SBMLSupport.getNthListOfProducts(i));
                FillIdListFromStringLists(_Modifiers, SBMLSupport.getNthListOfModifiers(i));

                int numParameters = SBMLSupport.getNumParameters(i);

                for (int j = 0; j < numParameters; ++j)
                {
                    _ParameterIds.Add(SBMLSupport.getNthParameterId(i, j));
                    _ParameterValues.Add(SBMLSupport.getNthParameterValue(i, j));
                }
                
                for (int j = 0; j < SBMLSupport.getNumParameters(i); ++j)
                {
                    _ParameterIds.Add(SBMLSupport.getNthParameterId(i, j));
                    _ParameterValues.Add(SBMLSupport.getNthParameterValue(i, j));
                }
                for (int j = 0; j < SBMLSupport.getNumReactants(i); ++j)
                {
                    _ReactantStoichiometry.Add(SBMLSupport.getNthReactantStoichiometryDouble(i, j));
                }
                for (int j = 0; j < SBMLSupport.getNumProducts(i); ++j)
                {
                    _ProductStoichiometry.Add(SBMLSupport.getNthProductStoichiometryDouble(i, j));
                }

                _CurveSegments = new List<NamedPair>();

                try
                {
                    int nGlyphIndex = GetGlyphIndexForId(_Name);
                    int nSpeciesReferences = (int)SBMLLayout.getReactionGlyph(nGlyphIndex)[2];
                    
                    List<LineSegment> oSegments = ConvertLineSegments(SBMLLayout.getReactionCurve(nGlyphIndex));
                    _CurveSegments.Add(new NamedPair("middleSegment", oSegments));

                    for (int j = 0; j < nSpeciesReferences; j++)
                    {
                        AddSpeciesReferences(_CurveSegments, nGlyphIndex, j);
                    }
                }
                catch (Exception )
                {
                    // DUNNO
                }
                
                // add modifiers
                foreach (NamedPair pair in _CurveSegments)
                {
                    if (pair.Role == null) continue;
                    string pairRoleToLower = pair.Role.ToLower();
                    if (pairRoleToLower.Contains("activator") || pairRoleToLower.Contains("inhibitor") || pairRoleToLower.Contains("modifier"))
                    {
                        if (!Modifiers.Contains( pair.Name ))
                        {
                            Modifiers.Add(pair.Name);
                        }
                    }
                }


            }

            private void FillIdListFromStringLists(List<string> oTarget, ArrayList oSource)
            {
                foreach (string s in oSource)
                    oTarget.Add(s);
            }

            private void AddSpeciesReferences(List<NamedPair> oCurveSegments,int nGlyphIndex,int nSpeciesReference)
            {
 	            ArrayList oList = SBMLLayout.getSpeciesReference(nGlyphIndex, nSpeciesReference);
                string sReference = (string)oList[1];
                List<LineSegment> oSegments = ConvertLineSegments(SBMLLayout.getSpeciesReferenceCurve(nGlyphIndex, nSpeciesReference));

                oCurveSegments.Add(new NamedPair(sReference, oSegments, (string)oList[3]));
            }

            static List<LineSegment> ConvertLineSegments(ArrayList[] oRawSegments)
            {
                List<LineSegment> oResult = new List<LineSegment>();
                foreach (ArrayList oList in oRawSegments)
                {
                    oResult.Add(ConvertLineSegment(oList));
                }
                return oResult;
            }

            static LineSegment ConvertLineSegment(ArrayList oList)
            {
                string sType = (string)oList[0];
                PointF oStart = ConvertToPoint((double[])oList[1]);
                PointF oEnd = ConvertToPoint((double[])oList[2]);
                if (sType == "LineSegment")
                {
                    return new LineSegment(oStart, oEnd);
                }
                return new BezierSegment(oStart, oEnd, ConvertToPoint((double[])oList[3]), ConvertToPoint((double[])oList[4]) );
            }
            static int GetGlyphIndexForId(string sId)
            {
                for (int i = 0; i < SBMLLayout.getNumberOfReactionGlyphs(); i++)
                {
                	ArrayList oList = SBMLLayout.getReactionGlyph(i);
                    if ((string)oList[1] == sId ) return i;
                }
                throw new ArgumentException("No ReactionGlyph for given ID");
            }

            static void FillIdListFromArrayLists(List<string> oTarget, ArrayList oSource)
            {
                foreach (ArrayList list in oSource)
                    oTarget.Add((string)list[0]);
            }
        }

    }
}
